#pragma once

#include <iostream>
#include <queue>
#include "Log.hpp"
#include "Thread.hpp"
#include "Cond.hpp"

namespace ThreadPoolModule
{
    using namespace ThreadModule;
    using namespace LogModule;
    const int gnum = 5; // 默认最多5个线程
    template <class T>
    class ThreadPool
    {
        ThreadPool(int num = gnum)
            : _num(num),
              _isruning(false),
              _sleepnum(0)
        {
            // 初始化时直接给创建线程，函数用lambda
            for (int i = 0; i < _num; i++)
            {
                _threads.emplace_back([this]()
                                      { HandleTask(); });
            }
        }

        void WakeAllThread()
        {
            MutexGuard mutexguard(&_mutex);
            if (_sleepnum > 0)
                _cond.Broadcast();
            LOG(LogLevel::DEBUG) << "唤醒所有的线程";
        }

        void WakeOneThread()
        {
            _cond.Signal();
            LOG(LogLevel::DEBUG) << "唤醒一个线程";
        }

        void Start()
        {
            if (_isruning)
                return; // 如果已经运行，退出
            _isruning = true;
            for (auto &thread : _threads)
            {
                thread.Start();
                LOG(LogLevel::DEBUG) << "start new thread success" << thread.GetName();
            }
        }

        // 注意一定要禁止拷贝构造和赋值
        ThreadPool(const ThreadPool<T> &) = delete;               // 禁止拷贝
        ThreadPool<T> &operator=(const ThreadPool<T> &) = delete; // 禁止赋值
    public:
        //懒汉方式下的单例模式：因为是多个线程，所有的线程一块只允许创建一次
        //所以就决不能是类内的成员方法,必须是静态的
        static ThreadPool<T> *GetInstance()
        {
            if(_instance == nullptr)
            {
                MutexGuard mutexguard(&_lock);
                LOG(LogLevel::DEBUG) << "获取单例";
                if(_instance == nullptr)
                {
                    //首次获取单例，创建后运行
                    LOG(LogLevel::DEBUG)<<"首次使用单例，创建";
                    _instance = new ThreadPool<T>();
                    _instance->Start();
                }
            }
            return _instance;
        }
        
        void Stop()
        {
            //停止，要先注意任务是否都完成了
            if(!_isruning)
            {
                return;//已经停下了就不需要了
            }
            _isruning = false;
            WakeAllThread();
        }
        void Join()
        {
            for (auto &thread : _threads)
            {
                thread.Join();
            }
        }
        void HandleTask()
        {
            char name[128];
            pthread_getname_np(pthread_self(), name, sizeof(name)); // 之前的Routine设置了线程的名字
            while (true)
            {
                T t;
                {
                    MutexGuard mutexguard(&_mutex);
                    // 线程池退出需要同时满足两种情况
                    // 1.线程池的任务全部完成
                    // 2._isruning == fasle
                    //使用while防止虚唤醒
                    while (_taskq.empty() && _isruning) // 当任务队列为空，且线程池正在运行，没有任务进入休眠
                    {
                        _sleepnum++;
                        //启动之后是没有任务的，全部的线程都会在这里等待
                        _cond.Wait(_mutex);//唤醒之后再这里，任务队列不为空，跳出这个循环
                        _sleepnum--;
                    }
                    // 如果不运行了，且任务队列为空了
                    if (!_isruning && _taskq.empty())
                    {
                        LOG(LogLevel::DEBUG) << name << "退出了 线程池退出&&任务队列为空";
                        break;
                    }
                    t = _taskq.front();
                    _taskq.pop();
                }
                // 因为要去保证效率，注意不要在锁的范围内去进行调用函数
                t();
            }
        }
        //入任务
        bool Equeue(const T& t)
        {
            //注意如果已经关闭了线程池，我就不能再去接收任务了
            if(_isruning)
            {
                MutexGuard mutexguard(&_mutex);
                _taskq.push(t);//入一个任务就要去唤醒一个线程
                if(_sleepnum == _threads.size())
                {
                    WakeOneThread();
                }
                return true;
            }
            return false;
        }

        ~ThreadPool()
        {}

    private:
        std::vector<Thread> _threads; // 池子
        int _num;
        std::queue<T> _taskq;
        Mutex _mutex;
        Cond _cond;
        bool _isruning;
        int _sleepnum;

        static ThreadPool<T> *_instance;
        static Mutex _lock;
    };
    template<class T>
    ThreadPool<T>* ThreadPool<T>::_instance = nullptr;

    template<class T>
    Mutex ThreadPool<T>::_lock;
}